feat(zerogit): auto-create a conventional branch before push/pr on default branch - #671
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds fail-closed Git branch detection and creation APIs. The CLI now selects feature branches for ChangesAutomatic feature branch routing
Formatter execution
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant runChangesPush
participant ensureFeatureBranch
participant zerogit
participant Push
User->>runChangesPush: invoke changes push
runChangesPush->>ensureFeatureBranch: determine branch target
ensureFeatureBranch->>zerogit: resolve default branch and remote state
ensureFeatureBranch->>zerogit: create computed feature branch
runChangesPush->>Push: publish branch with new-remote protection
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
internal/zerogit/zerogit_test.go (2)
836-848: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
CurrentGitUser's fallback branches (OS username, literal"user") are untested.Only the
git config user.namesuccess path is covered. The two fallback tiers (L715-718 in zerogit.go) have no coverage, at least a case where the fake runner errors/returns empty output to exercise the OS-username path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 836 - 848, Extend TestCurrentGitUser to cover CurrentGitUser’s fallback behavior when git config returns an error or empty output, asserting the OS-username fallback and command invocation; also add coverage for the final literal "user" fallback when no OS username is available.
731-792: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMissing coverage for
CreateBranchfailing to check out (e.g. branch already exists).All three subtests exercise success/dry-run/empty-name paths; none exercise the
git checkout -bfailure branch (L700-702 in zerogit.go), which is exactly the scenario that occurs on a name collision. Worth adding a subtest that returns a non-nil error/non-zero exit from the checkout call and asserts the wrapped error message.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 731 - 792, The TestCreateBranch coverage should include checkout failure, such as an existing branch name. Add a subtest alongside HappyPath and DryRunDoesNotCheckout using fakeRunner to return a non-zero/error result for the checkout invocation, then assert CreateBranch returns an error containing the wrapped checkout failure message.internal/cli/workflows.go (1)
1029-1060: 🚀 Performance & Scalability | 🔵 TrivialExtra LLM round-trip added to every default-branch push/PR without
--yes.When on the default branch (and no
--yes/dry-run),ensureFeatureBranchnow performs its ownStreamCompletioncall to name the branch, on top of any existing commit-message-generation LLM call in the same flow. That's an additional ~60s-capped network round trip and provider cost on a very common path (any push straight frommain). Worth being aware of for latency/cost budgeting, and consider whether the fallback slug is "good enough" to skip the LLM call by default (e.g., behind a flag) rather than always attempting it whenever a provider is configured.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1029 - 1060, The ensureFeatureBranch flow should not automatically perform an LLM request on every default-branch push or PR. Use fallbackBranchSlug(summary) by default and gate generateAutoBranchSlug behind an explicit opt-in configuration or flag, preserving the existing branch-generation behavior when that opt-in is enabled.internal/cli/workflow_test.go (1)
918-980: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNo CLI-level test for
runChangesPR+ensureFeatureBranchintegration.Coverage is added for
ensureFeatureBranchin isolation and forrunChangesPush(Lines 918-980), butrunChangesPRalso now routes throughensureFeatureBranchand forwards the ensured branch intoPushOptions.Branch(workflows.go Lines 924-928, 937). GivenprhardcodesdryRun=falseunlikepush, this path deserves its own targeted test (e.g. mirroringTestRunChangesPushCreatesFeatureBranchWhenOnDefaultforchanges pron the default branch) to lock in the new behavior before it ships.Want me to draft that test?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` around lines 918 - 980, The CLI tests cover feature-branch creation for runChangesPush but not the equivalent runChangesPR flow. Add a targeted test for changes pr on the default branch, verifying ensureFeatureBranch creates the expected branch and that runChangesPR forwards it through PushOptions.Branch, while preserving the existing dryRun=false behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/cli/workflows.go`:
- Around line 1087-1116: Update generateAutoBranchSlug to normalize
collected.Text before passing it to zerogit.SlugifyBranchComponent: select the
first non-empty line, trim surrounding whitespace and quotes, then slugify that
single-line value. Preserve the existing empty-slug error handling and provider
error propagation.
In `@internal/zerogit/zerogit.go`:
- Around line 637-662: Bound the remote lookup performed by IsDefaultBranch,
especially the isDefaultBranch call that may execute git ls-remote, with a short
context timeout even when the caller supplies context.Background(). On timeout
or remote lookup failure, preserve the existing local main/master fallback so
changes push/pr do not stall.
- Around line 677-704: Update CreateBranch to handle an already-existing local
branch before treating branch creation as failed: detect whether the requested
name exists locally, check it out and return the same BranchResult when it does,
while retaining checkout -b for new branches and preserving DryRun behavior.
---
Nitpick comments:
In `@internal/cli/workflow_test.go`:
- Around line 918-980: The CLI tests cover feature-branch creation for
runChangesPush but not the equivalent runChangesPR flow. Add a targeted test for
changes pr on the default branch, verifying ensureFeatureBranch creates the
expected branch and that runChangesPR forwards it through PushOptions.Branch,
while preserving the existing dryRun=false behavior.
In `@internal/cli/workflows.go`:
- Around line 1029-1060: The ensureFeatureBranch flow should not automatically
perform an LLM request on every default-branch push or PR. Use
fallbackBranchSlug(summary) by default and gate generateAutoBranchSlug behind an
explicit opt-in configuration or flag, preserving the existing branch-generation
behavior when that opt-in is enabled.
In `@internal/zerogit/zerogit_test.go`:
- Around line 836-848: Extend TestCurrentGitUser to cover CurrentGitUser’s
fallback behavior when git config returns an error or empty output, asserting
the OS-username fallback and command invocation; also add coverage for the final
literal "user" fallback when no OS username is available.
- Around line 731-792: The TestCreateBranch coverage should include checkout
failure, such as an existing branch name. Add a subtest alongside HappyPath and
DryRunDoesNotCheckout using fakeRunner to return a non-zero/error result for the
checkout invocation, then assert CreateBranch returns an error containing the
wrapped checkout failure message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f58092c2-d708-4e9f-b966-136d674d41ae
📒 Files selected for processing (5)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
|
Addressed the review, including the nitpicks. Actionable:
Nitpicks fixed:
One nitpick I'm leaving as-is: the extra LLM round-trip on every default-branch push without --yes. That's the intended behavior, not a bug: the LLM slug is the actual point of this feature, --yes/--dry-run already bypass it for anyone who wants to skip it, and gating it behind a further flag is new config surface for a cost/latency observation rather than a correctness issue. go vet, go build ./..., and go test -race -count=1 ./internal/zerogit/... ./internal/cli/... are all clean. |
jatmn
left a comment
There was a problem hiding this comment.
I found issues that need to be addressed before this is ready.
Findings
-
[P1] Keep branch generation from selecting an unrelated local branch
internal/cli/workflows.go:1042-1063,internal/zerogit/zerogit.go:709-718
changes pushnormally runs afterchanges commit, so the working tree inspected here is clean and the fallback name is alwaysuser/changes(the provider path likewise receives an empty diff). On a later push, or whenever a low-entropy fallback/LLM name already exists locally,CreateBranchchecks out that arbitrary existing ref instead of creating a branch at the current default-branch HEAD. The subsequent push/PR then publishes the stale branch while leaving the user's new commit onmain. Do not treat a name collision as a retry unless its history is proven to be the intended current work; use a unique name or fail visibly, and cover the ordinary commit-then-push sequence. -
[P1] Preserve and use the actual target remote throughout auto-branching
internal/cli/workflows.go:866-878,internal/cli/workflows.go:924-940,internal/cli/workflows.go:1034,internal/zerogit/zerogit.go:574-585
The preflight always checksorigin, while the original push may target--remoteor the current branch's configured upstream. After creating a branch, that new branch has no tracking configuration, soPushfalls back tooriginas well. In a fork/upstream setup this can either leave the advertised default-branch dead end intact (the target remote identifies the branch as default after theoriginpreflight skipped creation) or push/create the PR againstoriginrather than the source branch's upstream. Resolve the remote once before branching, pass it toIsDefaultBranch, and pass the same resolved remote toPush; add non-origin/upstream coverage. -
[P1] Do not fail open when default-branch lookup times out
internal/zerogit/zerogit.go:616-629
The new five-second deadline applies to the existing guard inPushas well as the new preflight. Ifls-remote --symreftakes longer than five seconds for a repository whose default istrunk/develop, the fallback only recognizesmainandmaster;Pushthen proceeds without--yes. Before this change it waited for the remote answer and preserved the confirmation guard. A lookup timeout needs to fail closed (or use a trusted local default reference), not be treated as evidence that an arbitrary branch is unprotected. -
[P1] Do not upload a diff during ordinary push/PR without an explicit opt-in
internal/cli/workflows.go:1048-1055
A configured provider now causes every default-branchchanges push/changes prto send the full change diff to that provider. These commands were previously git-only; the existing LLM behavior is explicitly requested throughchanges commit --auto.redactChangeSummaryremoves secret-shaped values but intentionally retains ordinary source code, so this silently exports proprietary code (and JSON mode gives no notice). Keep the deterministic local naming path by default and require an explicit LLM opt-in before constructing this completion request.
|
Pushed e3ec76f (plus gofmt fixup 68bcce3) for all four findings.
go build, go vet, and the zerogit and cli suites pass locally. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/zerogit/zerogit_test.go (1)
527-668: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMissing coverage for
Push's new fail-closed error path.
TestPushBranchesToRemoteexercisesisDefault == true(RejectsDefaultBranch) andisDefault == false(HappyPath,FlagsForceAndDryRun,FallbackRemoteToOrigin), but none of the subtests driveisDefaultBranchinto returning an error (remote lookup fails and no localrefs/remotes/<remote>/HEADrecord) to verifyPush's new wrapping at Line 585-587 ("cannot verify %q is not the default/protected branch: %w; use --yes to override"). This is the actual new logic added toPushin this PR and is otherwise only exercised indirectly viaIsDefaultBranch's ownFailsClosedWhenDefaultBranchUnknowntest, which doesn't go throughPush.✅ Suggested additional subtest
t.Run("FailsWhenDefaultBranchCannotBeVerified", func(t *testing.T) { root := t.TempDir() runner := &fakeRunner{results: []CommandResult{ {Stdout: root + "\n"}, {Stdout: "feat/some-feature\n"}, {Stdout: "origin\n"}, {ExitCode: 128, Stderr: "fatal:"}, // ls-remote fails {ExitCode: 1}, // no local refs/remotes/origin/HEAD record }} _, err := Push(context.Background(), PushOptions{ Cwd: root, RunGit: runner.Run, }) if err == nil || !strings.Contains(err.Error(), "use --yes to override") { t.Fatalf("expected fail-closed error, got %v", err) } })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 527 - 668, Add a FailsWhenDefaultBranchCannotBeVerified subtest to TestPushBranchesToRemote that makes ls-remote fail and the local refs/remotes/origin/HEAD lookup fail, then assert Push returns an error containing “use --yes to override.” Ensure the fake runner sequence reaches Push’s wrapped isDefaultBranch error path rather than the existing successful or protected-branch paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@internal/zerogit/zerogit_test.go`:
- Around line 527-668: Add a FailsWhenDefaultBranchCannotBeVerified subtest to
TestPushBranchesToRemote that makes ls-remote fail and the local
refs/remotes/origin/HEAD lookup fail, then assert Push returns an error
containing “use --yes to override.” Ensure the fake runner sequence reaches
Push’s wrapped isDefaultBranch error path rather than the existing successful or
protected-branch paths.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 16f16bb3-6e00-490a-b235-ab360363f2d3
📒 Files selected for processing (5)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
- internal/cli/app.go
- internal/cli/workflows.go
|
Pushed a subtest for CodeRabbit's coverage nitpick: TestPushBranchesToRemote/FailsWhenDefaultBranchCannotBeVerified drives the remote lookup and the local refs/remotes/origin/HEAD record to fail and asserts Push itself refuses with the use --yes guidance, exercising the new fail-closed wrapping directly rather than only through IsDefaultBranch. |
There was a problem hiding this comment.
Approving. The feature is well-gated branch creation skips on --yes/--dry-run, and the LLM naming path is opt-in via --auto with the diff redacted before it leaves the machine and I like that you hardened isDefaultBranch to fail closed instead of silently downgrading to the main/master name heuristic when the remote lookup times out. Two small things worth a glance: isDefaultBranch now short-circuits on the literal names main/master before consulting the remote, so a repo whose real default is trunk would treat a local feature branch named main as the default (safe direction it only blocks a push, never permits one but slightly surprising); and if the LLM slug generation fails after printing "Generating branch name using LLM..." it silently falls back to the deterministic slug with no follow-up message, which could confuse a user waiting on the LLM. Neither blocks merge.
jatmn
left a comment
There was a problem hiding this comment.
I found issues that need to be addressed before this is ready.
Findings
-
[P1] Keep an unreachable remote from clearing the default-branch guard
internal/zerogit/zerogit.go:639-646
A localrefs/remotes/<remote>/HEADis a cache, not evidence that a differently named branch is safe. If a server renames its default frommaintotrunk, the local record remainsorigin/HEAD -> origin/main, andls-remotethen times out or fails, pushingtrunkreturnsfalsehere. Both the preflight andPushrepeat that result, so the command pushes the newly protected branch without--yes, despite the intended fail-closed behavior. Only use a cached match to block a branch; when it does not match after live verification fails, return the unknown-default error. -
[P1] Honor
--diff-bytesbefore sending a branch-name prompt to the provider
internal/cli/workflows.go:870,928,1057,1072-1079,1115-1121
changes push/praccept and document--diff-bytes, but neither call threadsoptions.maxDiffBytesintoensureFeatureBranch, which invokesInspectwith onlyCwd. With--auto, the resulting unboundedsummary.Diffis embedded in the provider request. A user who supplies a cap to limit proprietary source sent for LLM naming therefore uploads the complete diff; pass the option through toInspectOptionsjust as the commit path does. -
[P2] Refuse an auto-branch push when there is no commit to publish
internal/cli/workflows.go:1057-1096
The new path branches solely from working-tree status and never establishes that HEAD is ahead of the selected default branch. On a clean, up-to-date default branch it creates and pushesuser/<HEAD-subject>at exactly the default tip; with only uncommitted edits it names a branch from those edits but pushes the unchanged HEAD, leaving the edits local.changes prthen leaves that remote branch behind before GitHub rejects the empty comparison. Check that there is a publishable commit range (and report no changes otherwise) before creating or pushing the branch. -
[P2] Do not treat an LLM preamble as the generated branch slug
internal/cli/workflows.go:1144-1155
The helper claims to tolerate non-compliant model responses but returns the first non-empty line verbatim. A common reply such asHere is a suggested branch name:\nadd-login-pagecreatesuser/here-is-a-suggested-branch-name; a fenced reply begins with```and falls back silently. Parse a valid slug line or strip the supported wrappers before slugifying, and cover preamble and fenced responses.
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
|
Pushed fixes for the review findings:
|
5b0131b to
a079996
Compare
a079996
jatmn
left a comment
There was a problem hiding this comment.
I found issues that need to be addressed before this is ready.
Findings
-
[P1] Terminate options before passing the remote to
ls-remote
internal/zerogit/zerogit.go:630
remotecan come from--remote=<value>or branch configuration, but it is inserted before theHEADpositional argument without--. A value such as--upload-pack=/bin/echois parsed by Git as an option;git ls-remote --symref --upload-pack=/bin/echo HEADinvokes that program (and fails with its output as a protocol error). Put--before the remote and add a dash-prefixed-remote regression test so this preflight cannot turn a remote value into Git options. -
[P1] Avoid colliding with branches that exist only on the target remote
internal/zerogit/zerogit.go:756
The suffix loop probes onlyrefs/heads/<name>locally, thenensureFeatureBranchpushes the generated name to the resolved remote. A pre-existing remote-only<user>/<slug>(for example an old/open PR whose tip is already inmain, or a branch absent after pruning) is not detected;git push -ucan fast-forward it and silently append the new work to that unrelated remote branch/PR. Check the chosen names against the target remote as well, or fail before creating/pushing, and cover a remote-only collision. -
[P1] Permit a first feature-branch push to an empty remote
internal/zerogit/zerogit.go:640
An empty newly created remote has neither aHEADsymref fromls-remotenor a localrefs/remotes/<remote>/HEAD. After the new flow creates a non-default feature branch,Pushtherefore returns “default branch … unknown” and requires--yes;git remote set-head --autocannot repair an empty remote. This regresses the first push of a safe non-default branch and conflicts with the preflight’s stated legitimate-first-push path. Distinguish an unborn remote (or otherwise allow this non-default first push) while keeping the default-branch guard fail-closed.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
This round landed well — the suffix-based collision handling, threading the resolved remote through the default-branch check and the push, gating the LLM naming behind --auto, and the preamble/fence slug parsing are all what I wanted to see, and CI is green across the board. But I'm with jatmn on his latest pass, so holding approval. The one I care most about: a brand-new empty remote is now a dead end — ls-remote returns no symref, there's no local refs/remotes//HEAD, and the error's own suggested fix (git remote set-head --auto) can't work on an empty remote, so the very first push of a fresh repo needs --yes. That's the exact dead-end UX this PR exists to remove; please carve out the unborn-remote case while keeping the guard fail-closed otherwise (zerogit.go ~640-656). Second, the suffix loop only probes local refs/heads, so a stale same-name branch that exists only on the target remote (an old merged PR branch, say) gets silently fast-forwarded by push -u and inherits the new commits — probe the remote too, or fail before pushing (zerogit.go ~756). The missing -- before the remote in ls-remote predates this PR, but you rewrote that function and already did it right in Push's args, so add it while you're in there. My earlier two notes (the main/master short-circuit and the quiet LLM fallback) still don't gate.
|
Pushed aabd6f2 for the three open items: (1) isDefaultBranch terminates options with -- before the remote (dash-prefixed-remote regression test included); (2) an unborn remote — ls-remote succeeds with zero refs — now counts as proof there is no protected default, so the first feature-branch push of a fresh repo is no longer a --yes dead end, while every failure path stays fail-closed and main/master stay guarded by the name heuristic; (3) CreateBranch probes the target remote's heads once (bounded, with --) so a remote-only stale branch counts as taken in the suffix loop, and an unreachable remote fails visibly before anything is created or pushed. |
jatmn
left a comment
There was a problem hiding this comment.
I found issues that need to be addressed before this is ready.
Findings
-
[P1] Do not treat a missing remote HEAD symref as proof that the remote is empty
internal/zerogit/zerogit.go:642
git ls-remote --symref <remote> HEADalso returns no output when a non-empty remote has a dangling or missing HEAD symref. In that state this returnsfalse, nil, which clears the new fail-closed default-branch guard for branches such astrunk;Push/ensureFeatureBranchcan then publish without--yes. Confirm that the remote has no heads before allowing the unborn-repository exception, or keep the default branch unknown and require an explicit override. -
[P1] Refuse when the working tree cannot be represented by the branch being pushed
internal/cli/workflows.go:1069
This only checks whetherHEADis ahead and then names a branch from the working-tree snapshot, butCreateBranchandPushpublish commits only. With an ahead commit plus additional unstaged edits, the command creates a PR named after the edits while omitting them; iforigin/mainis absent locally, the ignoredCommitsAheaderror permits the same empty-branch result with only uncommitted changes. Require a clean working tree (or explicitly commit/stage it) and fail when the publishable commit range cannot be determined, rather than silently creating a partial or empty PR. -
[P2] Make the remote branch collision check atomic with the push
internal/zerogit/zerogit.go:784
The remote-head snapshot is taken before checkout, while the ordinarygit push -uoccurs later. Another client can create the same generated name in that window; when its ref is at the default tip, this push fast-forwards it and silently appends this work to the other branch/PR. Use a push-time “destination must not exist” condition (or retry a fresh suffix after rejection) so the collision protection cannot be bypassed by a concurrent creator. -
[P2] Do not make ordinary feature-branch pushes depend on a fixed five-second HEAD lookup
internal/zerogit/zerogit.go:628
Every non-main/masterPushnow givesls-remotefive seconds. A reachable SSH/VPN remote whose handshake or credential negotiation exceeds that limit falls through to the fail-closed error and requires--yesbefore the normal push can even run; the same cap also blocks the new collision probe. Honor a caller/user deadline or otherwise avoid turning a slow, established remote into a default-branch-verification failure.
|
Pushed 130b986. This addresses the latest review, all four points. Fixed:
Nothing left open, all four points from the latest review are addressed. |
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Re-verified on the current head. All three of my asks are resolved. The unborn/empty-remote case is carved out and now confirmed with a second ls-remote --heads probe before the fail-closed guard is cleared, remote-only collisions are detected and made race-safe with a zero-value force-with-lease, and the missing -- before the remote in the ls-remote preflight is in. It also picked up jatmn's later findings (the HEAD-symref-as-empty case and the unrepresentable-working-tree one).
CI is green and CodeRabbit approved on this head. My two remaining notes (a repo whose real default is "trunk" being treated as a feature branch, and the message left dangling if LLM slug generation fails) are non-gating. Approving.
521d4ba
|
Pushed 521d4ba (on top of 130b986) for the remaining item from the latest review. From 130b986 (already on this head when the review was filed against aabd6f2):
From 521d4ba (this push):
Covered by |
521d4ba to
ef8e072
Compare
|
Addressed the latest review findings: [P1] Missing remote HEAD symref is not proof the remote is empty [P1] Refuse when the working tree cannot be represented by the branch being pushed [P2] Remote branch collision check atomic with push [P2] Ordinary feature-branch pushes no longer depend on a fixed 5s HEAD lookup Also rebased onto current |
Push -u can publish a remote branch then fail to write local upstream config. Treat that as incomplete, recover with set-upstream-to, and probe the remote before reasserting the nonexistence lease on retry. Also skip one-word LLM acknowledgements before accepting a slug-shaped line. Refs Gitlawb#671
|
Addressed the latest review on tip
Tests: Push recovery/failure cases, real-git |
Replace the local-state inference chain (upstream config, a zeroAutoBranch marker, per-remote provenance tracking) that kept producing new collision-safety edge cases across many review rounds with a single live remoteHasBranch check against the destination remote: missing there gets the nonexistence lease, already there gets a plain push, and git's own fast-forward check is the safety net if that existing branch isn't actually ours. Removes MarkGeneratedBranch/IsGeneratedBranch, now dead, along with the tests that pinned the specific local-state heuristics rather than the collision-safety property itself.
|
Pushed a rework of the branch-collision lease logic. After 17 review rounds each finding a new edge case in the local-state inference (upstream config, the zeroAutoBranch marker, per-remote provenance), I replaced the whole heuristic chain with a single live check against the destination remote right before deciding: missing there gets the nonexistence lease (protects a concurrent creator of the same name), already there gets a plain push, and git's own fast-forward check is the safety net if that existing branch turns out not to be ours (clear non-fast-forward error instead of an attempted silent recovery). This removes MarkGeneratedBranch/IsGeneratedBranch entirely (nothing reads the marker anymore, so nothing needs to write it) and replaces the test battery that pinned specific local-state heuristics with tests pinning the collision-safety property itself. Net -245 lines. Everything else (unborn-remote handling, default-branch restore-after-create, --yes bypass, slug extraction) is untouched. One real behavior change worth flagging explicitly: a manually checked-out branch now also gets the nonexistence lease on its first push, not just auto-generated ones — strictly safer, but a change in what gets protected. Verified with go build ./..., go vet ./..., gofmt, and go test ./internal/cli/... ./internal/zerogit/... (all pass). |
|
@coderabbitai full review |
✅ Action performedFull review finished. Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 28 minutes. |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (9)
internal/cli/workflow_test.go (2)
824-824: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffFour consecutive bool arguments are easy to transpose.
ensureFeatureBranch(ctx, w, jsonMode, cwd, remote, allowDefaultBranch, dryRun, autoNaming, maxDiffBytes, deps)is called with four bare bools in a row at about fifteen new call sites. A swap betweendryRunandautoNamingstill compiles and silently exercises a different path. Consider a small options struct for the flags, or a test helper with named fields.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` at line 824, Update the ensureFeatureBranch call sites in workflow tests, including the call in this test, to avoid consecutive positional boolean arguments by introducing a named options struct or test helper for jsonMode, allowDefaultBranch, dryRun, and autoNaming. Preserve each test’s existing flag values while making the argument names explicit.
2100-2120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winIsolate this test from the developer's global git config. The test runs real
git commitandgit pushwith onlyuser.nameanduser.emailset locally. A contributor withcommit.gpgsign = true, a customcore.hooksPath,init.templateDir, or apre-commithook in their global config gets a failing test that has nothing to do with the change.git init --bare -b mainalso requires git 2.28 or newer, so an older git fails instead of skipping.Set
GIT_CONFIG_GLOBALandGIT_CONFIG_SYSTEMto a non-existent path inrunWorkflowGitand in the CLI invocation, and disable signing explicitly.🧪 Proposed hermetic setup
func runWorkflowGit(t *testing.T, dir string, args ...string) string { t.Helper() cmd := exec.Command("git", args...) cmd.Dir = dir + cmd.Env = append(os.Environ(), + "GIT_CONFIG_GLOBAL="+filepath.Join(t.TempDir(), "nonexistent-gitconfig"), + "GIT_CONFIG_SYSTEM="+filepath.Join(t.TempDir(), "nonexistent-gitsystem"), + "GIT_CONFIG_NOSYSTEM=1", + ) out, err := cmd.CombinedOutput()runWorkflowGit(t, repo, "config", "user.email", "zero@example.invalid") + runWorkflowGit(t, repo, "config", "commit.gpgsign", "false") + runWorkflowGit(t, repo, "config", "core.hooksPath", filepath.Join(tmp, "no-hooks"))Note that
runWithDepsruns git through the production code path, so the same environment isolation must reach it for full hermeticity.As per coding guidelines: "Code and tests must pass on Linux, macOS, and Windows."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` around lines 2100 - 2120, Make TestRunChangesBareRemotePushThenPRUsable hermetic by configuring GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to nonexistent paths in runWorkflowGit and the CLI invocation, including the runWithDeps production path. Explicitly disable commit signing and skip the test when the installed Git does not support git init --bare -b main, while preserving the existing test behavior.Source: Coding guidelines
internal/cli/workflows.go (3)
527-540: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe
--messagecheck is now duplicated. Lines 527-529 fully subsume the precedingoptions.message != ""check, and both emit the same error text. Remove the older check to keep one rule per flag.♻️ Proposed cleanup
- if command != "commit" && options.message != "" { - return options, false, execUsageError{"--message is only valid with `zero changes commit`"} - } if command != "commit" && options.hasMessage { return options, false, execUsageError{"--message is only valid with `zero changes commit`"} }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 527 - 540, Remove the redundant older options.message != "" validation near the command-option checks, retaining the existing `command != "commit" && options.hasMessage` rule and its error message as the single --message validation. Leave the surrounding --auto and --dry-run checks unchanged.
1344-1389: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
firstLineandplausibleLinecan never differ. Both are assigned under!isPreambleText(candidate), and the extra clause(!isPreambleText(line) || candidate != line)on Line 1378 is implied by!isPreambleText(candidate): whencandidate == linethe two tests are identical, and whencandidate != linethe first clause is already true.plausibleLineis therefore always set wheneverfirstLineis set, so thereturn firstLineon Line 1388 is unreachable except for the empty case. Collapse them into one fallback variable.Related edge case:
isPreambleTexttreats any line ending in.,!, or?as preamble, so a reply ofadd login page.yields an empty slug and an error fromgenerateAutoBranchSlug. Consider trimming trailing sentence punctuation before the preamble test.♻️ Proposed simplification
func extractBranchSlug(text string) string { - var firstLine string - var plausibleLine string + var fallback string for _, line := range strings.Split(text, "\n") { @@ if slugLineRe.MatchString(candidate) && !isPreambleText(candidate) { return candidate } - if firstLine == "" && !isPreambleText(candidate) { - firstLine = candidate - } - - if (!isPreambleText(line) || candidate != line) && !isPreambleText(candidate) { - if plausibleLine == "" { - plausibleLine = candidate - } - } + if fallback == "" && !isPreambleText(candidate) { + fallback = candidate + } } - if plausibleLine != "" { - return plausibleLine - } - return firstLine + return fallback }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1344 - 1389, In extractBranchSlug, collapse firstLine and plausibleLine into a single fallback variable because their current conditions are equivalent, and return that variable when no slug-shaped candidate is found. Before applying isPreambleText, trim trailing sentence punctuation from candidates so values such as “add login page.” remain eligible for slug extraction and generateAutoBranchSlug does not receive an empty result.
1265-1277: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse
path.Basefor git paths.summary.Files[0].Pathcomes from git and always uses/separators.filepath.Baseapplies OS-specific separator rules, so the same input can split differently on Windows. Usepath.Basefor a platform-independent result.♻️ Proposed change
- return zerogit.SlugifyBranchComponent(filepath.Base(summary.Files[0].Path)) + return zerogit.SlugifyBranchComponent(path.Base(summary.Files[0].Path))As per coding guidelines: "Code and tests must pass on Linux, macOS, and Windows; canonicalize paths before comparison and avoid asserting raw temporary-directory spellings."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1265 - 1277, Update fallbackBranchSlug to use path.Base instead of filepath.Base when extracting the filename from summary.Files[0].Path, preserving platform-independent handling of git’s slash-separated paths.Source: Coding guidelines
internal/zerogit/zerogit.go (2)
745-752: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared remote-resolution logic.
This block duplicates the remote resolution in
Pushat lines 581-588. The doc comment at lines 720-724 states the two must resolve remotes identically, so any future edit must touch both sites. Extract one helper, for exampleresolveRemoteForBranch(ctx, runGit, root, branch, options.Remote), and call it from both functions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit.go` around lines 745 - 752, Extract the shared remote-resolution logic into a helper such as resolveRemoteForBranch, accepting the context, Git runner, repository root, branch, and configured remote. Replace the inline resolution blocks in both Push and the current function around the branch remote lookup with calls to this helper, preserving the existing configured-remote, branch-config, and origin fallback behavior.
998-1006: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winTerminate option parsing in
DeleteBranchfor consistency.Every other new helper in this file passes
--before remote or ref values.DeleteBranchpassesfallbackBranchandbranchToDeletedirectly. A branch name that starts with-would then be parsed as an option. Add--to both commands.🛡️ Proposed hardening
- if _, err := gitOutput(ctx, runGit, cwd, "checkout", fallbackBranch); err != nil { + if _, err := gitOutput(ctx, runGit, cwd, "checkout", "--", fallbackBranch); err != nil { return err } - _, err := gitOutput(ctx, runGit, cwd, "branch", "-D", branchToDelete) + _, err := gitOutput(ctx, runGit, cwd, "branch", "-D", "--", branchToDelete) return errNote:
git checkout -- <name>restores paths rather than switching branches. Usegit switch -- <name>or validate the name instead; verify the chosen form before applying.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit.go` around lines 998 - 1006, Update DeleteBranch to terminate git option parsing for both fallbackBranch and branchToDelete. Use an option-safe branch-switching command that preserves switching to the fallback branch, then pass -- before both branch values in the switch and delete commands; do not use checkout --, which changes the command’s meaning.internal/cli/app.go (1)
92-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winName the parameters in these function types.
Fields such as
commitsAhead func(context.Context, string, string, string) (int, error)andremoteHasBranch func(context.Context, string, string, string) (bool, error)give no hint of thecwd, remote, branchorder. A caller that swapsremoteandbranchcompiles cleanly and probes the wrong ref. Add parameter names to the type, for examplefunc(ctx context.Context, cwd, remote, branch string) (bool, error).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/app.go` around lines 92 - 105, Update the function type declarations in the surrounding struct, especially commitsAhead and remoteHasBranch, to name each parameter and make the cwd, remote, and branch ordering explicit. Apply descriptive names consistently to the other context and string parameters without changing any signatures or behavior.internal/zerogit/zerogit_test.go (1)
1537-1559: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winGuard the
branch.autoSetupMerge=inheritdependency.If Git versions older than 2.35 are supported, skip this test or report the required Git version before using
inherit. Git 2.35 introduced this value; older versions can reject it or fail to createorigin/main, causing a misleading test failure.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 1537 - 1559, Update TestHasUpstreamRejectsInheritedMainUpstream to detect the installed Git version before configuring branch.autoSetupMerge=inherit, and skip the test with a clear required-version message when Git is older than 2.35. Perform this check before the inherit configuration or dependent checkout, while preserving the existing test behavior for supported Git versions.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/cli/workflow_test.go`:
- Around line 914-945: The TestExtractBranchSlug table is missing regression
coverage for punctuation-terminated suggestions and ensureFeatureBranch fallback
failures. Add a case proving “add login page.” is handled as the intended slug
rather than rejected as preamble, and add an ensureFeatureBranch test covering
both provider errors and empty generated slugs, asserting deterministic fallback
and the expected notice or logging behavior.
In `@internal/cli/workflows.go`:
- Around line 1211-1225: Update the auto-naming block around
generateAutoBranchSlug to track whether the LLM path produced a slug and print a
short fallback notice to stdout when it does not. Cover resolveConfig failures,
unavailable provider profiles, newProvider failures, generation errors, and
empty results, while preserving the existing deterministic slug fallback and
suppressing notices in jsonMode.
- Around line 1251-1258: Update the rollback handling in the resetBranchRef
failure path to capture the error returned by deleteBranch instead of discarding
it. When branch deletion also fails, include both the restore and rollback
failures in the returned error and clearly indicate that manual repair is
required; preserve the existing restore-only error when deletion succeeds or is
unavailable.
In `@internal/zerogit/zerogit_test.go`:
- Around line 1184-1194: Update the comment in the
ReturnsErrorWhenRemoteTrackingRefMissing test to state that a missing
remote-tracking ref causes CommitsAhead to return an error and callers fail
closed by refusing to proceed with the push. Remove the inaccurate “cannot tell”
and “proceeds” description while preserving the test behavior.
In `@internal/zerogit/zerogit.go`:
- Around line 783-805: Update the CreateBranch comment and DryRun behavior so
they agree: either describe DryRun as returning the requested trimmed branch
name without collision resolution, or move collision resolution before the
DryRun return so it returns the name a real run would create; preserve
non-dry-run collision handling.
- Around line 621-638: Gate the upstream verification and repair block after the
push in Push on !options.DryRun, leaving the existing UpstreamRef and branch
--set-upstream-to behavior unchanged for real pushes. Add a dry-run regression
test covering an unpublished branch that verifies no branch --set-upstream-to
command is issued and the push remains successful.
---
Nitpick comments:
In `@internal/cli/app.go`:
- Around line 92-105: Update the function type declarations in the surrounding
struct, especially commitsAhead and remoteHasBranch, to name each parameter and
make the cwd, remote, and branch ordering explicit. Apply descriptive names
consistently to the other context and string parameters without changing any
signatures or behavior.
In `@internal/cli/workflow_test.go`:
- Line 824: Update the ensureFeatureBranch call sites in workflow tests,
including the call in this test, to avoid consecutive positional boolean
arguments by introducing a named options struct or test helper for jsonMode,
allowDefaultBranch, dryRun, and autoNaming. Preserve each test’s existing flag
values while making the argument names explicit.
- Around line 2100-2120: Make TestRunChangesBareRemotePushThenPRUsable hermetic
by configuring GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to nonexistent paths in
runWorkflowGit and the CLI invocation, including the runWithDeps production
path. Explicitly disable commit signing and skip the test when the installed Git
does not support git init --bare -b main, while preserving the existing test
behavior.
In `@internal/cli/workflows.go`:
- Around line 527-540: Remove the redundant older options.message != ""
validation near the command-option checks, retaining the existing `command !=
"commit" && options.hasMessage` rule and its error message as the single
--message validation. Leave the surrounding --auto and --dry-run checks
unchanged.
- Around line 1344-1389: In extractBranchSlug, collapse firstLine and
plausibleLine into a single fallback variable because their current conditions
are equivalent, and return that variable when no slug-shaped candidate is found.
Before applying isPreambleText, trim trailing sentence punctuation from
candidates so values such as “add login page.” remain eligible for slug
extraction and generateAutoBranchSlug does not receive an empty result.
- Around line 1265-1277: Update fallbackBranchSlug to use path.Base instead of
filepath.Base when extracting the filename from summary.Files[0].Path,
preserving platform-independent handling of git’s slash-separated paths.
In `@internal/zerogit/zerogit_test.go`:
- Around line 1537-1559: Update TestHasUpstreamRejectsInheritedMainUpstream to
detect the installed Git version before configuring
branch.autoSetupMerge=inherit, and skip the test with a clear required-version
message when Git is older than 2.35. Perform this check before the inherit
configuration or dependent checkout, while preserving the existing test behavior
for supported Git versions.
In `@internal/zerogit/zerogit.go`:
- Around line 745-752: Extract the shared remote-resolution logic into a helper
such as resolveRemoteForBranch, accepting the context, Git runner, repository
root, branch, and configured remote. Replace the inline resolution blocks in
both Push and the current function around the branch remote lookup with calls to
this helper, preserving the existing configured-remote, branch-config, and
origin fallback behavior.
- Around line 998-1006: Update DeleteBranch to terminate git option parsing for
both fallbackBranch and branchToDelete. Use an option-safe branch-switching
command that preserves switching to the fallback branch, then pass -- before
both branch values in the switch and delete commands; do not use checkout --,
which changes the command’s meaning.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d5f1ebc0-9a2a-4ac7-baae-69128f1609ed
📒 Files selected for processing (5)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
Recover trailing-punctuation LLM slugs, notice deterministic fallback, report dual restore/rollback failures, and skip Push upstream repair on dry-run. Align comments with fail-closed and DryRun behavior. Refs Gitlawb#671
CodeRabbit findings addressed (173b327)
Verification: @coderabbitai full review |
Rate Limit Exceeded
|
…mat-on-write Use the binary path resolved by exec.LookPath and set formatter.Stdin to an empty reader so format-on-write does not hang on inherited stdin handles on Windows. Refs Gitlawb#671
|
@coderabbitai full review |
✅ Action performedFull review finished. Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 8 minutes. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
internal/cli/workflows.go (5)
960-964: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDocument why an unborn-probe error is ignored here.
Line 961 blocks only when
unbornErr == nil && unborn. A probe failure lets the flow continue to push and PR creation. That is a deliberate open-fail on a preflight whose only job is a better error message, and the later push still fails closed. The surrounding comment does not say so. Add one sentence, because every neighboring check in this PR fails closed and a reader will assume this one does too.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 960 - 964, Add a concise comment above the isUnbornRemote check explaining that probe errors are intentionally ignored so the flow can continue, with the later push/PR operation providing the fail-closed behavior; retain the existing condition and error handling unchanged.
930-966: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
changes prcontacts the remote twice for the same answer.Without
--yes, Line 941 callsdeps.isDefaultBranchonly to learn the remote.ensureFeatureBranchthen callsdeps.isDefaultBranchagain at Line 1111 with the same inputs. Each call runsgit ls-remote --symrefagainst the remote. The unborn probe adds a thirdls-remote, andrefreshTrackingRefadds a fetch. On a slow SSH remote the user waits for four sequential network round trips before anything is pushed.Thread the first result into the preflight instead of repeating the lookup. One option: add an optional pre-resolved default-branch state parameter to
ensureFeatureBranch, or move the unborn preflight inside it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 930 - 966, Avoid resolving the default branch twice in the changes pr workflow: reuse the result from the initial deps.isDefaultBranch call when invoking ensureFeatureBranch, or move the unborn-remote preflight into ensureFeatureBranch. Update ensureFeatureBranch and its callers as needed so the existing remote and branch behavior remains unchanged while eliminating the duplicate remote lookup.
1395-1409: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
plausibleLineandfirstLineare always equal, so the second branch is dead.At Line 1399 the guard reduces to
!isPreambleText(candidate): ifcandidate == line, the second clause already implies!isPreambleText(line); ifcandidate != line, the first clause is true by construction. That is the same condition as Line 1395. Both variables therefore receive the same first value, and theplausibleLinereturn at Line 1406 can never differ fromfirstLine.Collapse the two into one variable.
TestExtractBranchSlugshould keep passing unchanged, which confirms the redundancy.♻️ Proposed change
- if firstLine == "" && !isPreambleText(candidate) { - firstLine = candidate - } - - if (!isPreambleText(line) || candidate != line) && !isPreambleText(candidate) { - if plausibleLine == "" { - plausibleLine = candidate - } - } + if firstLine == "" && !isPreambleText(candidate) { + firstLine = candidate + } } - if plausibleLine != "" { - return plausibleLine - } return firstLineRemove the
plausibleLinedeclaration at Line 1359 as well.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1395 - 1409, In the branch-slug extraction logic, remove the redundant plausibleLine variable and its assignment condition, including its declaration. Keep a single firstLine value for the first non-preamble candidate and return it directly, preserving the existing behavior covered by TestExtractBranchSlug.
1106-1148: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNil-guarding is inconsistent across the injected Git dependencies.
remoteHasBranch,refreshTrackingRef,isUnbornRemote,branchUpstreamRef,resetBranchRef, anddeleteBranchare all nil-checked.inspectChanges,commitsAhead,headCommitSubject,currentGitUser, andcreateBranchare called directly.fillAppDepspopulates the second group, so production is safe, but a unit test that omits one of them panics instead of failing with a message. State the invariant in the doc comment, or guard both groups the same way.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1106 - 1148, Make dependency handling consistent in ensureFeatureBranch and the related workflow: either document in the relevant function comment that inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and createBranch are mandatory and must be populated by fillAppDeps, or add nil guards matching remoteHasBranch, refreshTrackingRef, isUnbornRemote, branchUpstreamRef, resetBranchRef, and deleteBranch. Ensure omitted injected dependencies return a clear error instead of panicking.
524-532: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCollapse the duplicated
--messagevalidation.Line 524 and Line 527 return the identical error.
options.message != ""is only reachable whenoptions.hasMessageis true, so the first check is redundant. Keep thehasMessageform, which also catches--message "".♻️ Proposed change
- if command != "commit" && options.message != "" { - return options, false, execUsageError{"--message is only valid with `zero changes commit`"} - } if command != "commit" && options.hasMessage { return options, false, execUsageError{"--message is only valid with `zero changes commit`"} }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 524 - 532, In the validation block of the workflow command parser, remove the redundant options.message != "" check and retain the options.hasMessage condition to reject --message for non-commit commands, including an explicitly empty value. Leave the subsequent commit --message and --auto conflict validation unchanged.internal/zerogit/zerogit.go (1)
873-884: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHarden option handling for branch and remote names.
- Do not add
--before the range inCommitsAhead;git rev-listtreats following arguments as paths and exits with status 129. Use--end-of-optionsor a fully qualified remote-tracking ref.- In
DeleteBranch, usegit switch -- <fallbackBranch>.git checkout -- <fallbackBranch>checks out a path, not a branch.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit.go` around lines 873 - 884, Harden ref handling in CommitsAhead and DeleteBranch: keep the rev-list range argument without --, but prevent option interpretation by using --end-of-options or a fully qualified remote-tracking ref; update DeleteBranch to switch to the fallback branch with git switch -- <fallbackBranch> rather than checkout, preserving branch semantics.internal/cli/workflow_test.go (1)
2226-2245: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winIsolate Git configuration for the entire test, not only
runWorkflowGit.Workflow Git commands also inherit the process environment, so global
branch.autoSetupMergecan change branch creation andcommit.gpgsignorcore.hooksPathcan break setup commits. Set the Git environment before setup and workflow execution. On Git 2.32+, useos.DevNullforGIT_CONFIG_GLOBALandGIT_CONFIG_SYSTEM; otherwise useGIT_CONFIG_NOSYSTEM=1with temporary global configuration paths. Skip Git versions older than 2.28 becausegit init --bare -b mainis unsupported.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` around lines 2226 - 2245, Isolate Git configuration for the entire TestRunChangesBareRemotePushThenPRUsable test, including repository setup and workflow execution, by configuring the process environment before any Git commands run. Use os.DevNull for GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM on Git 2.32+, or GIT_CONFIG_NOSYSTEM=1 with temporary global config paths on older supported versions; skip Git versions below 2.28 before using git init --bare -b main.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/tools/format_on_write.go`:
- Around line 81-90: Extend the tests for maybeFormatWrittenFile with a
formatter lookup failure case: set ZERO_FORMAT_ON_WRITE and PATH to a directory
that lacks gofmt, then assert the function returns writtenContent. Do not add a
separate nil-Stdin or EOF test.
---
Nitpick comments:
In `@internal/cli/workflow_test.go`:
- Around line 2226-2245: Isolate Git configuration for the entire
TestRunChangesBareRemotePushThenPRUsable test, including repository setup and
workflow execution, by configuring the process environment before any Git
commands run. Use os.DevNull for GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM on Git
2.32+, or GIT_CONFIG_NOSYSTEM=1 with temporary global config paths on older
supported versions; skip Git versions below 2.28 before using git init --bare -b
main.
In `@internal/cli/workflows.go`:
- Around line 960-964: Add a concise comment above the isUnbornRemote check
explaining that probe errors are intentionally ignored so the flow can continue,
with the later push/PR operation providing the fail-closed behavior; retain the
existing condition and error handling unchanged.
- Around line 930-966: Avoid resolving the default branch twice in the changes
pr workflow: reuse the result from the initial deps.isDefaultBranch call when
invoking ensureFeatureBranch, or move the unborn-remote preflight into
ensureFeatureBranch. Update ensureFeatureBranch and its callers as needed so the
existing remote and branch behavior remains unchanged while eliminating the
duplicate remote lookup.
- Around line 1395-1409: In the branch-slug extraction logic, remove the
redundant plausibleLine variable and its assignment condition, including its
declaration. Keep a single firstLine value for the first non-preamble candidate
and return it directly, preserving the existing behavior covered by
TestExtractBranchSlug.
- Around line 1106-1148: Make dependency handling consistent in
ensureFeatureBranch and the related workflow: either document in the relevant
function comment that inspectChanges, commitsAhead, headCommitSubject,
currentGitUser, and createBranch are mandatory and must be populated by
fillAppDeps, or add nil guards matching remoteHasBranch, refreshTrackingRef,
isUnbornRemote, branchUpstreamRef, resetBranchRef, and deleteBranch. Ensure
omitted injected dependencies return a clear error instead of panicking.
- Around line 524-532: In the validation block of the workflow command parser,
remove the redundant options.message != "" check and retain the
options.hasMessage condition to reject --message for non-commit commands,
including an explicitly empty value. Leave the subsequent commit --message and
--auto conflict validation unchanged.
In `@internal/zerogit/zerogit.go`:
- Around line 873-884: Harden ref handling in CommitsAhead and DeleteBranch:
keep the rev-list range argument without --, but prevent option interpretation
by using --end-of-options or a fully qualified remote-tracking ref; update
DeleteBranch to switch to the fallback branch with git switch --
<fallbackBranch> rather than checkout, preserving branch semantics.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: c147ff2e-ff04-4b0e-a705-a31fd831471c
📒 Files selected for processing (6)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/tools/format_on_write.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
Remove redundant command validation, eliminate duplicate remote lookup in PR workflow, harden ref options and branch switching, isolate test Git environment, and add formatter lookup test. Refs Gitlawb#671
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
internal/cli/workflows.go (1)
1091-1096: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFix the truncated sentence in the doc comment.
Line 1091 begins "The working tree must be clean and HEAD must be ahead of the resolved remote" and never ends. Line 1092 starts a separate sentence with the function name. Two drafts appear to have been merged.
📝 Proposed fix
-// The working tree must be clean and HEAD must be ahead of the resolved remote -// ensureFeatureBranch verifies working tree cleanliness, checks default branch -// state, and auto-creates a feature branch if on the default branch. The -// inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and -// createBranch fields on deps are mandatory dependencies populated by -// fillAppDeps. +// The working tree must be clean and HEAD must be ahead of the resolved +// remote branch; both are verified before any branch is created. The +// inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and +// createBranch fields on deps are mandatory dependencies populated by +// fillAppDeps.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflows.go` around lines 1091 - 1096, Complete the opening sentence of the doc comment before the “ensureFeatureBranch” sentence, preserving the intended requirements that the working tree is clean and HEAD is ahead of the resolved remote. Keep the existing dependency documentation unchanged.internal/zerogit/zerogit.go (1)
837-850: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winTerminate option parsing on
checkout -band validateName.Every other new remote-facing call in this file uses
--and rejects option-shaped values.CreateBranchdoes not.options.Nameis only trimmed. An exported caller that passes a name beginning with-makes Git parse it as an option. The current in-repo caller passeszerogit.BuildBranchNameoutput, which is safe, so this is hardening at the API boundary rather than a live defect.
ResetBranchRefalready validates its branch argument. Apply the same discipline here.🛡️ Proposed hardening
name := strings.TrimSpace(options.Name) if name == "" { return BranchResult{}, fmt.Errorf("branch name required") } + // refs/heads/<name> must stay inside the heads namespace, and the name + // must never reach `checkout` as an option. + if strings.HasPrefix(name, "-") || strings.HasPrefix(name, "/") || + strings.Contains(name, "..") || strings.ContainsAny(name, "\\ \t\n") { + return BranchResult{}, fmt.Errorf("invalid branch name %q", name) + }- if _, err := gitOutput(ctx, runGit, root, "checkout", "-b", name); err != nil { + if _, err := gitOutput(ctx, runGit, root, "checkout", "-b", name, "--"); err != nil { return BranchResult{}, fmt.Errorf("create branch %q: %w", name, err) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit.go` around lines 837 - 850, Harden CreateBranch by validating options.Name with the same branch-name validation used by ResetBranchRef, rejecting option-shaped or otherwise invalid names before Git commands run. Update the checkout -b invocation to terminate option parsing with -- while preserving the existing collision and branch-creation behavior.internal/cli/app.go (1)
99-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unused
branchHasUpstreamdependency wiring.
appDeps.branchHasUpstreamis only declared, wired indefaultAppDeps, and defaulted infillAppDeps; it is never called. Remove the field and its wiring if the feature branch flow only needsremoteHasBranch; keepzerogit.HasUpstreamand its tests.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/app.go` at line 99, Remove the unused appDeps.branchHasUpstream field and its wiring from defaultAppDeps and fillAppDeps, provided the feature branch flow relies only on remoteHasBranch. Preserve zerogit.HasUpstream and its existing tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/cli/workflows.go`:
- Around line 952-961: Remove the unreachable `if err != nil` check following
the `isUnbornRemote` probe in the workflow, since `ensureFeatureBranch` errors
are already handled earlier. Preserve the intentional `unbornErr` discard and
the existing unborn-remote and successful-flow behavior.
In `@internal/zerogit/zerogit_test.go`:
- Around line 1525-1618: Isolate Git configuration in
TestRemoteHasBranchSeesPushWithoutLocalUpstream and
TestHasUpstreamRejectsInheritedMainUpstream by adding the same three t.Setenv
calls used by TestRunChangesBareRemotePushThenPRUsable, covering
push.autoSetupRemote and branch.autoSetupMerge with platform-safe environment
handling. Ensure os is imported if needed, before either test invokes git.
---
Nitpick comments:
In `@internal/cli/app.go`:
- Line 99: Remove the unused appDeps.branchHasUpstream field and its wiring from
defaultAppDeps and fillAppDeps, provided the feature branch flow relies only on
remoteHasBranch. Preserve zerogit.HasUpstream and its existing tests.
In `@internal/cli/workflows.go`:
- Around line 1091-1096: Complete the opening sentence of the doc comment before
the “ensureFeatureBranch” sentence, preserving the intended requirements that
the working tree is clean and HEAD is ahead of the resolved remote. Keep the
existing dependency documentation unchanged.
In `@internal/zerogit/zerogit.go`:
- Around line 837-850: Harden CreateBranch by validating options.Name with the
same branch-name validation used by ResetBranchRef, rejecting option-shaped or
otherwise invalid names before Git commands run. Update the checkout -b
invocation to terminate option parsing with -- while preserving the existing
collision and branch-creation behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2e5861a6-53ab-44c7-8e86-9489e0c55078
📒 Files selected for processing (7)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/tools/format_on_write.gointernal/tools/format_on_write_test.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
Harden CreateBranch name validation, remove unreachable workflow error check, complete doc comment, isolate test Git environment, and clean up unused appDeps field. Refs Gitlawb#671
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
internal/cli/workflow_test.go (2)
2325-2341: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueBind
runWorkflowGitto the test deadline.
runGitCommandininternal/zerogit/zerogit_test.goderives a context fromt.Deadline()before running git.runWorkflowGituses a plainexec.Command. If a git subprocess hangs against the bare remote, this test blocks until the whole test binary times out, with no indication of which command stalled.🧪 Proposed change
func runWorkflowGit(t *testing.T, dir string, args ...string) string { t.Helper() - cmd := exec.Command("git", args...) + ctx := context.Background() + if deadline, ok := t.Deadline(); ok { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, deadline) + defer cancel() + } + cmd := exec.CommandContext(ctx, "git", args...) cmd.Dir = dir🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` around lines 2325 - 2341, Update runWorkflowGit to derive a context from t.Deadline() and run the git subprocess with exec.CommandContext instead of exec.Command, preserving the existing command arguments, working directory, and failure reporting.
828-828: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftConsider an options struct for
ensureFeatureBranch.Every call site passes nine positional arguments, including four consecutive bools:
jsonMode,allowDefaultBranch,dryRun,autoNaming. This file repeats that shape about twenty-five times. Transposing two of the bools compiles cleanly and silently changes which guard the test exercises, so a broken test can still pass for the wrong reason.Grouping the flags into a small options struct would make each call site self-documenting and make the bool order impossible to get wrong.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/workflow_test.go` at line 828, Introduce a small options struct for ensureFeatureBranch containing jsonMode, allowDefaultBranch, dryRun, and autoNaming, then update ensureFeatureBranch and all call sites in this test file to pass the struct instead of positional boolean arguments. Preserve existing behavior and values while making each option explicitly named.internal/cli/app.go (1)
640-641: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the restore opt-out explicit instead of relying on nil defaults.
fillAppDepsleavesdeleteBranchandresetBranchRefnil so unit tests do not touch a real git tree. Production is correct today becauseRunpassesdefaultAppDeps(). The fragility is thatensureFeatureBranchsilently skips the default-branch restore whenresetBranchRefis nil. Any future caller that builds a partialappDepsloses a correctness behavior with no signal: localmainkeeps the pushed commits and diverges after a squash-merge.Consider filling both from defaults like every other dependency and having tests inject explicit no-op stubs. That keeps the skip intentional and visible at the call site.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/cli/app.go` around lines 640 - 641, Update fillAppDeps to initialize deleteBranch and resetBranchRef from defaultAppDeps rather than leaving them nil, and adjust affected unit tests to inject explicit no-op stubs when real git restoration or deletion should be skipped. Preserve ensureFeatureBranch’s restore behavior for production and make test opt-outs explicit at their appDeps construction sites.internal/zerogit/zerogit_test.go (2)
763-789: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a case for the residual-upstream-mismatch branch.
SurfacesUpstreamWriteFailureWhenRecoveryFailscovers theset-upstream-tocommand failing. The second guard inPushis not covered:set-upstream-toexits 0 butUpstreamRefstill does not return<remote>/<branch>, which produces the "local upstream is still not" error. Add a fixture where the repair command succeeds and the follow-upUpstreamRefreturns a different value.🧪 Proposed additional subtest
t.Run("SurfacesUpstreamStillWrongAfterRepair", func(t *testing.T) { root := t.TempDir() runner := &fakeRunner{results: []CommandResult{ {Stdout: root + "\n"}, {Stdout: "user/slug\n"}, {Stdout: "origin\n"}, {Stdout: "ref: refs/heads/main\tHEAD\nabc123\tHEAD\n"}, {Stdout: "To origin\n * [new branch] user/slug -> user/slug\n"}, {ExitCode: 128, Stderr: "fatal: no upstream configured"}, {Stdout: ""}, // branch --set-upstream-to succeeds {Stdout: "origin/main\n"}, // but the upstream is still wrong }} _, err := Push(context.Background(), PushOptions{Cwd: root, RunGit: runner.Run}) if err == nil || !strings.Contains(err.Error(), "local upstream is still not") { t.Fatalf("expected residual-mismatch error, got %v", err) } })As per coding guidelines: "Every behavior or security-boundary change requires a regression test, including failure paths".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 763 - 789, Add a regression subtest alongside SurfacesUpstreamWriteFailureWhenRecoveryFails that exercises successful set-upstream-to recovery followed by UpstreamRef returning an incorrect remote/branch. Configure fakeRunner with the full command sequence, assert Push returns an error containing “local upstream is still not,” and preserve the existing test’s coverage for repair-command failure.Source: Coding guidelines
1461-1523: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider isolating ambient Git config in the
initGitRepo-based tests too.
TestRemoteHasBranchSeesPushWithoutLocalUpstreamandTestHasUpstreamRejectsInheritedMainUpstreampinGIT_CONFIG_GLOBAL,GIT_CONFIG_SYSTEM, andGIT_CONFIG_NOSYSTEM. The three tests here run the realgitbinary throughinitGitRepowithout that isolation. The risk is lower becausebranch -M mainnormalizes the branch name and no push occurs, so this is not blocking. Setting the same three variables insideinitGitRepowould make every real-git test in this file hermetic in one place.As per coding guidelines: "Code and tests must pass on Linux, macOS, and Windows".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit_test.go` around lines 1461 - 1523, Update initGitRepo, or the shared setup it uses for real-git tests, to isolate ambient Git configuration by setting GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to controlled test values and GIT_CONFIG_NOSYSTEM appropriately. Apply this centrally so TestResetBranchRefMovesDefaultWithoutTouchingFeature, TestResetBranchRefRefusesCheckedOutBranch, and TestCurrentBranchReturnsCheckedOutName inherit the same hermetic setup, while preserving cross-platform behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/cli/workflows.go`:
- Around line 868-871: Bound the feature-branch preflight context before calling
ensureFeatureBranch in runChangesPush and runChangesPR, replacing
context.Background() with a deadline-bearing context suitable for the blocking
git and remote operations. Apply the change at internal/cli/workflows.go lines
868-871 and 927-930, preserving existing cancellation and error handling.
In `@internal/tools/format_on_write_test.go`:
- Around line 84-90: Update TestFormatOnWriteFormatterLookupFailure to write the
expected Go source content to the a.go path before calling
maybeFormatWrittenFile. Store that content in a variable and compare the
returned value against the same variable, ensuring the test specifically
exercises formatter lookup failure rather than a nonexistent-file execution
failure.
---
Nitpick comments:
In `@internal/cli/app.go`:
- Around line 640-641: Update fillAppDeps to initialize deleteBranch and
resetBranchRef from defaultAppDeps rather than leaving them nil, and adjust
affected unit tests to inject explicit no-op stubs when real git restoration or
deletion should be skipped. Preserve ensureFeatureBranch’s restore behavior for
production and make test opt-outs explicit at their appDeps construction sites.
In `@internal/cli/workflow_test.go`:
- Around line 2325-2341: Update runWorkflowGit to derive a context from
t.Deadline() and run the git subprocess with exec.CommandContext instead of
exec.Command, preserving the existing command arguments, working directory, and
failure reporting.
- Line 828: Introduce a small options struct for ensureFeatureBranch containing
jsonMode, allowDefaultBranch, dryRun, and autoNaming, then update
ensureFeatureBranch and all call sites in this test file to pass the struct
instead of positional boolean arguments. Preserve existing behavior and values
while making each option explicitly named.
In `@internal/zerogit/zerogit_test.go`:
- Around line 763-789: Add a regression subtest alongside
SurfacesUpstreamWriteFailureWhenRecoveryFails that exercises successful
set-upstream-to recovery followed by UpstreamRef returning an incorrect
remote/branch. Configure fakeRunner with the full command sequence, assert Push
returns an error containing “local upstream is still not,” and preserve the
existing test’s coverage for repair-command failure.
- Around line 1461-1523: Update initGitRepo, or the shared setup it uses for
real-git tests, to isolate ambient Git configuration by setting
GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM to controlled test values and
GIT_CONFIG_NOSYSTEM appropriately. Apply this centrally so
TestResetBranchRefMovesDefaultWithoutTouchingFeature,
TestResetBranchRefRefusesCheckedOutBranch, and
TestCurrentBranchReturnsCheckedOutName inherit the same hermetic setup, while
preserving cross-platform behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5c88a954-d4ed-4b5a-8174-771fcbdfc612
📒 Files selected for processing (7)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/tools/format_on_write.gointernal/tools/format_on_write_test.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
…ings on PR 671 Use featureBranchOptions struct for ensureFeatureBranch, bound preflight contexts, add upstream repair mismatch subtest, and centralize test Git config isolation. Refs Gitlawb#671
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/zerogit/zerogit.go (1)
748-755: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared remote-resolution logic.
Push(lines 581-588) andIsDefaultBranchresolve the remote with identical code: explicit option, thenbranch.<name>.remote, then"origin". The CLI threads the remote fromIsDefaultBranchintoPush, so the two must stay identical. A single helper removes the risk of divergence.♻️ Proposed helper
func resolveRemoteForBranch(ctx context.Context, runGit Runner, root, requested, branch string) string { if remote := strings.TrimSpace(requested); remote != "" { return remote } if upstream, err := gitOutput(ctx, runGit, root, "config", "branch."+branch+".remote"); err == nil && upstream != "" { return upstream } return "origin" }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/zerogit/zerogit.go` around lines 748 - 755, Extract the duplicated remote-selection logic into a shared resolveRemoteForBranch helper, preserving the order of explicit trimmed remote, branch.<name>.remote configuration, and "origin" fallback. Update both Push and IsDefaultBranch to use this helper so their remote resolution remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/cli/workflows.go`:
- Around line 1106-1117: Complete the truncated dependency-contract comment by
stating that inspectChanges, commitsAhead, headCommitSubject, currentGitUser,
and createBranch are required non-nil dependencies, while the remaining listed
dependencies may be nil and are guarded. Move the entire comment block from
featureBranchOptions to directly above ensureFeatureBranch so its documentation
applies to the function.
---
Nitpick comments:
In `@internal/zerogit/zerogit.go`:
- Around line 748-755: Extract the duplicated remote-selection logic into a
shared resolveRemoteForBranch helper, preserving the order of explicit trimmed
remote, branch.<name>.remote configuration, and "origin" fallback. Update both
Push and IsDefaultBranch to use this helper so their remote resolution remains
identical.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d32f90b9-2e17-47c6-afed-de78d21919fe
📒 Files selected for processing (7)
internal/cli/app.gointernal/cli/workflow_test.gointernal/cli/workflows.gointernal/tools/format_on_write.gointernal/tools/format_on_write_test.gointernal/zerogit/zerogit.gointernal/zerogit/zerogit_test.go
| // The working tree must be clean and HEAD must be ahead of the resolved | ||
| // remote branch; both are verified before any branch is created. The | ||
| // inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and | ||
| type featureBranchOptions struct { | ||
| JSONMode bool | ||
| AllowDefaultBranch bool | ||
| DryRun bool | ||
| AutoNaming bool | ||
| MaxDiffBytes int | ||
| } | ||
|
|
||
| func ensureFeatureBranch(ctx context.Context, stdout io.Writer, workspaceRoot string, requestedRemote string, opts featureBranchOptions, deps appDeps) (string, string, bool, error) { |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Finish the truncated doc sentence and move it onto ensureFeatureBranch.
Line 1108 ends mid-sentence: "The inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and". The reader never learns the contract for those dependencies. That contract matters: ensureFeatureBranch calls inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and createBranch without nil guards, while it does guard isDefaultBranch, remoteHasBranch, refreshTrackingRef, isUnbornRemote, branchUpstreamRef, resetBranchRef, and deleteBranch. A nil value in the first group panics.
The block also documents a function but sits on the featureBranchOptions type declaration, so go doc featureBranchOptions prints function prose.
Complete the sentence and move the block below the type, directly above func ensureFeatureBranch.
As per coding guidelines: "Ensure PR descriptions, help text, and comments match shipped behavior".
📝 Proposed fix
-// The working tree must be clean and HEAD must be ahead of the resolved
-// remote branch; both are verified before any branch is created. The
-// inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and
type featureBranchOptions struct {
JSONMode bool
AllowDefaultBranch bool
DryRun bool
AutoNaming bool
MaxDiffBytes int
}
+// (…existing doc block moves here…)
+// The working tree must be clean and HEAD must be ahead of the resolved
+// remote branch; both are verified before any branch is created. The
+// inspectChanges, commitsAhead, headCommitSubject, currentGitUser, and
+// createBranch dependencies are required on the default-branch path and are
+// called without nil guards; fillAppDeps populates all of them. The
+// refreshTrackingRef, isUnbornRemote, branchUpstreamRef, resetBranchRef, and
+// deleteBranch dependencies are optional and are skipped when nil.
func ensureFeatureBranch(ctx context.Context, stdout io.Writer, workspaceRoot string, requestedRemote string, opts featureBranchOptions, deps appDeps) (string, string, bool, error) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@internal/cli/workflows.go` around lines 1106 - 1117, Complete the truncated
dependency-contract comment by stating that inspectChanges, commitsAhead,
headCommitSubject, currentGitUser, and createBranch are required non-nil
dependencies, while the remaining listed dependencies may be nil and are
guarded. Move the entire comment block from featureBranchOptions to directly
above ensureFeatureBranch so its documentation applies to the function.
Source: Coding guidelines
Summary
zero changes pushandzero changes prcurrently refuse to push straight to the default branch (main/master) but don't offer any alternative, so hitting that guard is a dead end.CreateBranch,IsDefaultBranch,CurrentGitUser,SlugifyBranchComponent, andBuildBranchNametointernal/zerogit.push/prnow call a newensureFeatureBranchstep: if the current branch is the default branch and neither--yesnor--dry-runwas passed, it generates a short slug for the diff (via the configured LLM provider, falling back to a deterministic slug derived from the changed files if no provider is configured) and checks out<git user>/<slug>before pushing.--yesand--dry-runbypass this entirely, preserving the existing refuse/preview behavior.Linked issue
None. This came out of a direct discussion about zero having no defined branch-naming convention, not a filed issue.
Test plan
go build ./...go vet ./...go test ./...(all green except a pre-existing, unrelated failure ininternal/contextreportthat also fails on unmodifiedmain)gofmt -lclean on all changed filesinternal/zerogit/zerogit_test.go(CreateBranch,IsDefaultBranch,CurrentGitUser,SlugifyBranchComponent,BuildBranchName)internal/cli/workflow_test.gocoveringensureFeatureBranchdirectly (default-branch creation with/without a provider, skip when already off default, skip on--yes/--dry-run) and end-to-end throughzero changes pushSummary by CodeRabbit
changes commit,changes push, andchanges prsupport automatic feature-branch creation.